Imports System.Reflection
Imports System.IO
Imports System.Threading
Imports System.Xml
Imports System.Data.SqlClient
Imports System.Collections
Imports System.Diagnostics

<Assembly: CLSCompliant(True)> 

Public Class UXAgent
#Region "Private Data"
    Private testDriver As Assembly = Nothing
    Private cn As New SqlConnection
#End Region

#Region "Form Methods"
    Private Sub UXAgent_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim dlgSplash As New Splash()
        Try
            Cursor = Cursors.AppStarting

            dlgSplash.Show()
            Application.DoEvents()

            txtUserName.Text = System.Security.Principal.WindowsIdentity.GetCurrent.Name.ToString

            MSP_EXT_NEW_GetCommandResultsTableAdapter.Connection = cn
            MSP_EXT_NEW_GetTestCommandsByTestNameTableAdapter.Connection = cn
            MSP_EXT_NEW_GetTestCommandParamsByTestCmdTableAdapter.Connection = cn
            MSP_EXT_NEW_GetAgentTestsByAgentTableAdapter.Connection = cn
            MSP_EXT_NEW_GetResultsTableAdapter.Connection = cn
            DsAgentCommands.EnforceConstraints = False

            UpdateDB(My.Settings.WW_COE_Test_FrameworkConnectionString)

            txtFileName.Text = My.Settings.TestAssemblyClassName
            txtClassName.Text = My.Settings.TestAssemblyClassName
            txtSession.Text = Guid.NewGuid().ToString()

            ' Load the test assembly specified in the app.config.
            LoadTestAssembly(txtFileName.Text, txtClassName.Text)

            AddAgent()
        Catch ex As Exception
            UpdateStatusBar(ex.Message, False)
        Finally
            dlgSplash.FadeForm()
            dlgSplash.Close()
            Cursor = Cursors.Default
        End Try
    End Sub

    Private Sub UXAgent_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        My.Settings.Save()
        'DeleteAgent()
        DeactivateAgent()
    End Sub
#End Region

#Region "Test Execution Methods"
    Private Sub ExecuteCommands(ByVal row As DataRow)
        Try
            ' Load the test assembly.
            LoadTestAssembly(row("AssemblyName").ToString, row("ClassName").ToString)

            Dim clock As New Stopwatch
            Dim Elapsed As Long
            Dim drResults As DataRow
            Dim dtExecute As DateTime
            Const minute As Long = 1000 * 60

            ' Retrieve and cache all the command and param data first.
            Dim cmds As New ArrayList
            Dim params As New ArrayList
            MSP_EXT_NEW_GetTestCommandsByTestNameTableAdapter.Fill(DsAgentCommands.MSP_EXT_NEW_GetTestCommandsByTestName, row("TestName").ToString)
            Dim dt As dsAgentCommands.MSP_EXT_NEW_GetTestCommandParamsByTestCmdDataTable
            For Each rowCmd As DataRow In DsAgentCommands.MSP_EXT_NEW_GetTestCommandsByTestName.Rows
                dt = New dsAgentCommands.MSP_EXT_NEW_GetTestCommandParamsByTestCmdDataTable
                MSP_EXT_NEW_GetTestCommandParamsByTestCmdTableAdapter.Fill(dt, row("TestName").ToString, rowCmd("ID"))
                cmds.Add(rowCmd("Name").ToString)
                params.Add(dt)
            Next

            clock.Start()
            If DsAgentCommands.MSP_EXT_NEW_GetTestCommandsByTestName.Rows.Count = 0 Then
                Exit Sub
            End If
            ' Create the test class in the late bound assembly to be used for the test.
            ' Assume that the class is the same for all commands.  The controller should enforce that constraint.
            Dim instance As Object = CreateTestClass(row)
            Do
                Try
                    ' Set status to executing
                    SetTestStatus(row("TestName").ToString, "Executing")
                    UpdateHistory("Begin executing " + row("TestName").ToString)

                    ' Get the test commands and params.
                    Dim tics As Long
                    Dim index As Integer = 0
                    For Each rowCmd As DataRow In DsAgentCommands.MSP_EXT_NEW_GetTestCommandsByTestName.Rows
                        dtExecute = DateTime.Now
                        tics = CallMethod(instance, row("ClassName").ToString, rowCmd("Name").ToString, params(index))
                        ' Log results to the UI.
                        UpdateHistory(rowCmd("Name").ToString + " executed in " + tics.ToString + " ms")
                        ' Save results to a DataSet for storage later.
                        drResults = DsAgentCommands.MSP_EXT_NEW_GetResults.Rows.Add()
                        drResults("TestName") = row("TestName").ToString
                        drResults("AgentName") = txtUserName.Text
                        drResults("Session") = txtSession.Text
                        drResults("CommandName") = rowCmd("Name").ToString
                        drResults("ExecuteDateTime") = dtExecute
                        drResults("ElapsedTime") = tics
                        index = index + 1
                    Next

                    ' Set status to executing
                    SetTestStatus(row("TestName").ToString, "Completed")
                    UpdateHistory("Finished executing " + row("TestName").ToString)

                Catch ex As Exception
                    MessageBox.Show(ex.Message)
                    SetTestStatus(row("TestName").ToString, "Failed")
                    UpdateHistory(row("TestName").ToString + " failed.")
                End Try
                Elapsed = clock.ElapsedMilliseconds()
            Loop Until Convert.ToInt32(row("Duration")) < (Elapsed / minute) Or Convert.ToBoolean(row("ExecuteOnce")) = True
            Try
                CallMethod(instance, row("ClassName").ToString, "Dispose", Nothing)
            Catch ex As Exception
                UpdateHistory("Dispose method for " + row("ClassName").ToString + " failed.")
            End Try

        Catch ex As Exception
            MessageBox.Show(ex.Message)
            SetTestStatus(row("TestName").ToString, "Failed")
            UpdateHistory(row("TestName").ToString + " failed.")
        End Try
    End Sub

    Private Function CreateTestClass(ByVal row As DataRow) As Object
        MSP_EXT_NEW_GetInitializationParamsTableAdapter.Fill(DsAgentCommands.MSP_EXT_NEW_GetInitializationParams, row("TestName").ToString)
        Dim index As Integer
        Dim params() As Object = Nothing

        If DsAgentCommands.MSP_EXT_NEW_GetInitializationParams.Rows.Count > 0 Then
            ReDim params(DsAgentCommands.MSP_EXT_NEW_GetInitializationParams.Rows.Count - 1)
        End If
        For Each dr As DataRow In DsAgentCommands.MSP_EXT_NEW_GetInitializationParams.Rows
            ' Build the parameter list.
            index = 0
            params(index) = Convert.ChangeType(dr("Value"), Type.GetType(dr("Type").ToString))
            index = index + 1
        Next
        ' Create an instance of the class with the param list for the constructor.
        Return Activator.CreateInstance(testDriver.GetType(row("ClassName").ToString), params)
    End Function

    Private Sub SetTestStatus(ByVal TestName As String, ByVal Status As String)
        Dim cn As New SqlConnection(txtConnStr.Text)
        Try
            cn.Open()
            Dim cmd As New SqlCommand("MSP_EXT_NEW_SetTestStatus", cn)
            cmd.CommandType = CommandType.StoredProcedure
            Dim param As New SqlParameter("@TestName", SqlDbType.Text)
            param.Value = TestName
            cmd.Parameters.Add(param)
            param = New SqlParameter("@Session", SqlDbType.UniqueIdentifier)
            param.Value = New Guid(txtSession.Text)
            cmd.Parameters.Add(param)
            param = New SqlParameter("@Status", SqlDbType.Text)
            param.Value = Status
            cmd.Parameters.Add(param)
            cmd.ExecuteNonQuery()
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        Finally
            cn.Close()
        End Try
    End Sub

    Private Sub uxAgentSettings_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles uxTimerAgentSettings.Tick
        ' Turn off polling while processing the tests.
        uxTimerAgentSettings.Enabled = False
        UpdateStatusBar("Polling at " + DateTime.Now().ToShortDateString + " " + DateTime.Now().ToShortTimeString(), False)

        ' Get any pending tests.
        Dim ExecuteImmediately As Boolean
        MSP_EXT_NEW_GetAgentTestsByAgentTableAdapter.Fill(DsAgentCommands.MSP_EXT_NEW_GetAgentTestsByAgent, New Guid(txtSession.Text), "Pending")
        For Each row As DataRow In DsAgentCommands.MSP_EXT_NEW_GetAgentTestsByAgent.Rows
            ' Check the immediate execution flags for delayed start.
            ExecuteImmediately = Convert.ToBoolean(row("ExecuteImmediately"))
            If ExecuteImmediately = True Or (ExecuteImmediately = False And Convert.ToDateTime(row("StartTime")) < DateTime.Now) Then
                ToolStripStatusLabel1.Text = "Executing test " + row("TestName").ToString + "."
                ExecuteCommands(row)
            End If
        Next

        ' Save the test results to the DB.
        MSP_EXT_NEW_GetResultsTableAdapter.Update(DsAgentCommands.MSP_EXT_NEW_GetResults)
        DsAgentCommands.AcceptChanges()

        ' Turn on polling after all queued tests are complete.
        uxTimerAgentSettings.Enabled = True
    End Sub

    Private Sub LoadTestAssembly(ByVal fileName As String, ByVal className As String)
        ' Look in the application directory for any test assemblies.
        Dim index As Integer = Application.ExecutablePath.LastIndexOf("\")
        Dim appDir As String = Application.ExecutablePath.Substring(0, index)

        txtFileName.Text = fileName
        txtClassName.Text = className

        Try
            ' Load the test assembly.
            testDriver = Nothing
            Dim fullName As String = appDir + "\" + fileName
            If File.Exists(fullName) Then
                testDriver = Assembly.LoadFile(fullName)
            End If
            If Not testDriver Is Nothing Then
                lbMethods.Items.Clear()
                ' Get the test class from the loaded assembly.
                Dim classType As Type = testDriver.GetType(className)
                If Not classType Is Nothing Then
                    ' Build a list of the available test methods in the class.
                    For Each mi As MethodInfo In classType.GetMethods()
                        If testDriver.GetType(className).ToString() = mi.DeclaringType.ToString() Then
                            ' Don't display the Dispose method.
                            If mi.Name <> "Dispose" Then
                                ' Build the parameter list for the display.
                                Dim numParams As Integer = 0
                                Dim itemStr As String = mi.Name + "("
                                For Each pi As ParameterInfo In mi.GetParameters
                                    numParams = numParams + 1
                                    If numParams > 1 Then
                                        itemStr = itemStr + ", "
                                    End If
                                    itemStr = String.Concat(itemStr, pi.Name, " as ", pi.ParameterType)
                                Next
                                itemStr = itemStr + ")"
                                lbMethods.Items.Add(itemStr)
                            End If
                        End If
                    Next
                End If
            End If
        Catch ex As Exception
            MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
        End Try
    End Sub

    Private Function CallMethodwithDialog(ByVal className As String, ByVal methodName As String) As Long
        ' Create an instance of the class.
        Dim instance As Object = Activator.CreateInstance(testDriver.GetType(className))
        Dim mi As MethodInfo = testDriver.GetType(txtClassName.Text).GetMethod(methodName)
        Dim numParams As Integer = 0
        Dim index As Integer
        Dim params() As Object = Nothing

        If mi.GetParameters.GetLength(0) > 0 Then
            ' Display the parameter dialog.
            Dim dlg As New ParamForm()
            For Each pi As ParameterInfo In mi.GetParameters
                index = dlg.dgParams.Rows.Add()
                dlg.dgParams.Rows(index).Cells("Name").Value = pi.Name
                dlg.dgParams.Rows(index).Cells("Type").Value = pi.ParameterType.ToString
                numParams = numParams + 1
            Next
            If dlg.ShowDialog = Windows.Forms.DialogResult.Cancel Then
                Return -1
            End If

            ' Build the parameter list.
            If dlg.dgParams.Rows.Count > 0 Then
                ReDim params(numParams - 1)
                index = 0
                ' Assume that the order of the params has not changed.  Sorting must be disabled in the grid.
                For Each pi As ParameterInfo In mi.GetParameters
                    params(index) = Convert.ChangeType(dlg.dgParams.Rows(index).Cells("Value").Value, pi.ParameterType)
                    index = index + 1
                Next
            End If
        End If

        ' Call the test method.
        Dim tics As Long
        tics = CType(mi.Invoke(instance, params), Long)
        Return tics
    End Function

    Private Function CallMethod(ByVal instance As Object, ByVal className As String, ByVal methodName As String, ByVal dsParams As TestFrameworkAgent.dsAgentCommands.MSP_EXT_NEW_GetTestCommandParamsByTestCmdDataTable) As Long
        ' Create an instance of the class.
        Dim mi As MethodInfo = testDriver.GetType(className).GetMethod(methodName)
        Dim numParams As Integer = 0
        Dim index As Integer
        Dim params() As Object = Nothing

        If mi.GetParameters.GetLength(0) > 0 Then
            ' Build the parameter list.
            ReDim params(dsParams.Rows.Count - 1)
            index = 0
            ' Assume that the order of the params has not changed.  Sorting must be disabled in the grid.
            For Each pi As ParameterInfo In mi.GetParameters
                params(index) = Convert.ChangeType(dsParams.Rows(index)("Value"), pi.ParameterType)
                index = index + 1
            Next
        End If

        ' Call the test method.
        Dim tics As Long
        tics = CType(mi.Invoke(instance, params), Long)
        Return tics
    End Function
#End Region

#Region "Utility Functions"
    Private Sub AddAgent()
        Dim cmd As New SqlCommand()
        cmd.Connection = cn
        cmd.CommandText = "MSP_EXT_NEW_AddAgent"
        cmd.CommandType = CommandType.StoredProcedure
        Dim param As New SqlParameter("@Machine", SqlDbType.NVarChar, 50)
        param.Value = Environment.MachineName
        cmd.Parameters.Add(param)
        param = New SqlParameter("@UserName", SqlDbType.NVarChar, 50)
        param.Value = txtUserName.Text
        cmd.Parameters.Add(param)
        param = New SqlParameter("@StartupTime", SqlDbType.DateTime)
        param.Value = DateTime.Now()
        cmd.Parameters.Add(param)
        param = New SqlParameter("@Session", SqlDbType.UniqueIdentifier)
        param.Value = New Guid(txtSession.Text)
        cmd.Parameters.Add(param)
        cmd.ExecuteNonQuery()
    End Sub

    Private Sub DeleteAgent()
        Dim cmd As New SqlCommand()
        cmd.Connection = cn
        cmd.CommandText = "MSP_EXT_NEW_DeleteAgent"
        cmd.CommandType = CommandType.StoredProcedure
        Dim param As New SqlParameter("@Session", SqlDbType.UniqueIdentifier)
        param.Value = New Guid(txtSession.Text)
        cmd.Parameters.Add(param)
        cmd.ExecuteNonQuery()
    End Sub

    Private Sub DeactivateAgent()
        Dim cmd As New SqlCommand()
        cmd.Connection = cn
        cmd.CommandText = "MSP_EXT_NEW_DeactivateAgent"
        cmd.CommandType = CommandType.StoredProcedure
        Dim param As New SqlParameter("@Session", SqlDbType.UniqueIdentifier)
        param.Value = New Guid(txtSession.Text)
        cmd.Parameters.Add(param)
        param = New SqlParameter("@StopdownTime", SqlDbType.DateTime)
        param.Value = DateTime.Now()
        cmd.Parameters.Add(param)
        cmd.ExecuteNonQuery()
    End Sub

    Private Function CheckForDuplicateProcess(ByVal processName As String) As Boolean
        'function returns true if it finds more than one 'processName' running
        Dim Procs() As Process
        Dim proc As Process    'get ALL processes running on this machine in all desktops
        'this also finds all services running as well.

        Procs = Process.GetProcesses()
        Dim count As Integer = 0
        For Each proc In Procs
            If proc.ProcessName.ToString.Equals(processName) Then
                count += 1
            End If
        Next proc
        If count > 1 Then
            Return True
        Else
            Return False
        End If
    End Function

    Private Sub UpdateHistory(ByVal msg As String)
        lbHistory.Items.Insert(0, DateTime.Now.ToShortDateString + " " + DateTime.Now.ToLongTimeString + " " + msg)
        If (lbHistory.Items.Count > 500) Then
            lbHistory.Items.RemoveAt(lbHistory.Items.Count - 1)
        End If
    End Sub

    Private Sub UpdateDB(ByVal connStr As String)
        Try
            Cursor = Cursors.WaitCursor
            ToolStripStatusLabel1.Text = "Connecting to the database..."
            ToolStripStatusLabel1.ForeColor = Color.Black
            uxTimerAgentSettings.Enabled = False

            If txtConnStr.Text <> connStr Then
                If cn.State = ConnectionState.Open Then
                    cn.Close()
                End If
                txtConnStr.Text = connStr
                cn.ConnectionString = connStr
                cn.Open()

                TBControl.SelectTab(0)
            End If

            UpdateStatusBar("Connected to the database successfully.  Polling for tests.", False)
            uxTimerAgentSettings.Enabled = True
            TBControl.Enabled = True
        Catch ex As Exception
            UpdateStatusBar("Failed to connect to the database.", True)
            TBControl.Enabled = False
        Finally
            Cursor = Cursors.Default
        End Try
    End Sub

    Private Sub UpdateStatusBar(ByVal msg As String, ByVal err As Boolean)
        ToolStripStatusLabel1.Text = msg
        If err Then
            ToolStripStatusLabel1.ForeColor = Color.Red
        Else
            ToolStripStatusLabel1.ForeColor = Color.Black
        End If
        Application.DoEvents()
    End Sub

    Private Sub chkPolling_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkPolling.CheckedChanged
        uxTimerAgentSettings.Enabled = chkPolling.Checked
        If uxTimerAgentSettings.Enabled Then
            UpdateStatusBar("Polling for tests.", False)
        Else
            UpdateStatusBar("Polling for tests disabled.", True)
        End If
    End Sub
#End Region

#Region "Menu and Button Handlers"
    Private Sub btnClearHistory_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearHistory.Click
        lbHistory.Items.Clear()
    End Sub

    Private Sub menuClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuClose.Click
        Me.Close()
    End Sub

    Private Sub menuSettings_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuSettings.Click
        Dim dlg As New SettingsDialog
        If dlg.ShowDialog() = Windows.Forms.DialogResult.OK Then
            UpdateDB(dlg.ConnectionString)

            txtFileName.Text = dlg.txtFileName.Text
            txtClassName.Text = dlg.txtClassName.Text

            LoadTestAssembly(txtFileName.Text, txtClassName.Text)
        End If
    End Sub
#End Region

#Region "Tab Control"
    Private Sub TBControl_Selected(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TabControlEventArgs) Handles TBControl.Selected
        Select Case e.TabPage.Text
            Case "Results"
                Cursor = Cursors.WaitCursor
                MSP_EXT_NEW_GetCommandResultsTableAdapter.Fill(DsAgentCommands.MSP_EXT_NEW_GetCommandResults, New Guid(txtSession.Text))
                Cursor = Cursors.Default
        End Select
    End Sub
#End Region

#Region "Notify Icon"
    Private Sub notifyIcon_MouseDoubleClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles notifyIcon.MouseDoubleClick
        Visible = Not Visible
        If Visible Then
            WindowState = FormWindowState.Normal
        End If
    End Sub

    Private Sub UXAgent_Resize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Resize
        If Size = MinimumSize Then
            Visible = True
        Else
            Visible = False
        End If
    End Sub
#End Region
End Class


